<?php
  /* This class will embody a set of ADfunctions. Keeping them in here reduces
     the possibility of conflicts and makes rollout easier. Class should be
     called statically.
     
     @author Charles.Fulton@kzoo.edu
     @date December 4, 2007
  */
  
  // Standard data object class
  require_once('class.DataWrapper.php');
  
  class ADfunctions {
        public static $allow_anon_bind = false;
        public static $binduser = "charles";
        public static $bindpass = "nitle321";
        public static $bindbase = "o=lafayette";
        public static $host = "hack1.lafayette.edu";
        public static $options = array("LDAP_OPT_PROTOCOL_VERSION" => 3, "LDAP_OPT_REFERRALS" => 0);
        
        /*  This function performs a one-to-one search; given an unambiguous user field
            and value, it returns a discrete value for a requested field.
            
            If $user_field is not supplied the function assumes the $user_value is a CN. If
            there is not a discrete result an empty string is returned.
            
            @param string $user_value
            @param string $search_field
            @param string $user_field
            @return string
        */
        public static function getField($user_value, $search_field, $user_field="cn") {
          $data = self::getFields(array($user_field => $user_value),array($search_field));
          if(count($data) == 1) {
            return current($data)->get_property($search_field);
          } else return '';
        }
        
        /*  This function peforms a many-to-many search; given multiple search
            criteria and multiple return fields it returns an array of DataWrapper
            objects.
            
            If nothing is found an empty array is returned.
            
            @param array $search_fields
            @param array $return_fields
            @return array
        */
        public static function getFields($search_fields, $return_fields) {
          $data = array();
          if($ds = self::connect()) {
            $info = self::executeSearch($ds, $search_fields, $return_fields);
            foreach($info as $row) {
              if(is_array($row)) {
                $item = new DataWrapper();
                for($i=0;$i<$row["count"];$i++) {
                  $item->add_property($row[$i],$row[$row[$i]][0]);
                }
                array_push($data, $item);
              }
            }
          }
          ldap_close($ds);
          return $data;
        }
        
        /*  This function returns an array of groups (CN) related to a discrete user.
        
            If unset, $user_field assumed to be CN.
            
            @param string $user_value
            @param string $user_field
            @return array
        */
        public static function getGroups($user_value, $user_field="cn") {
          $groups = array();
          if($ds = self::connect()) {
            $info = self::executeSearch($ds, array($user_field => $user_value),array("memberof"));
            for($i=0;$i<$info[0]["memberof"]["count"];$i++) {
              $items = explode(",",$info[0]["memberof"][$i]);
              $groups[] = substr($items[0],strpos($items[0],"=") + 1);
            }
          }
          ldap_close($ds);
          return $groups;
        }
        
        /* This functions returns all members associated with the given group as an array of usernames (CN).
        
           @param string $search_group
           @return array
        */
        public static function getMembers($search_group) {
          $members = array();
          if($ds = self::connect()) {
            $info = self::executeSearch($ds, array("cn" => $search_group));
            for($i=0;$i<$info[0]["member"]["count"];$i++) {
              $items = explode(",",$info[0]["member"][$i]);
              $members[] = substr($items[0],strpos($items[0],"=") + 1);
            }
          }
          ldap_close($ds);
          return $members;
        }
        
        /*  This function checks if a single user is a member of the given group.
            
            If $user_field is not given, it is assumed to be CN. Distributions are ignored.
            
            @param string $user_value
            @param string $search_group
            @param string $user_field
            @return boolean
        */            
        public static function isMember($user_value, $search_group, $user_field="cn") {
          if($ds = self::connect()) {
            $info = self::executeSearch($ds, array($user_field => $user_value), array("memberof"));
            foreach($info[0]["memberof"] as $item) {
              if((strpos($item,"N=" .$search_group,1))&&(!strpos($item,"OU=Distributions"))) {
                ldap_close($ds);
                return true;
              }
            }
          }
          return false;
          ldap_close($ds);
        }
        
        /*  Peforms the actual ldap search and returns an array of entries. This should
            never be called directly.
            
            Returns an array of results. Result parsing is left to the caller function.
            
            @param resource $ds
            @param array $search_fields
            @param array $return_fields
        */
        public static function executeSearch($ds, $search_fields, $return_fields=array()) {
          $search_string = '';
          foreach($search_fields as $field => $value) {
            $search_string .= "($field=$value)";  
          }
          if(count($search_fields) > 1) {
            $search_string = "(&".$search_string.")";
          }
          $search_result = ldap_search($ds, self::$bindbase, $search_string, $return_fields);
          return ldap_get_entries($ds, $search_result);
        }
        
        /*  Establishes a connection to an LDAP resource and binds with given
            username and password. If $allow_anon_bind is set to true, an
            anonymous bind will bet attempted if $binduser and $bindpass are not set.
            
            Returns a resource handle or false on failure.
            
            @return mixed
        */
        public static function connect() {
          $ds = ldap_connect(self::$host);
          foreach(self::$options as $name => $value) {
            ldap_set_option($ds, $name, $value);
          }   
          if($ds) {
            if(self::$binduser=='' && self::$bindpass == '') {
              if(self::$allow_anon_bind) {
                ldap_bind($ds);
              } else {
                return false;
              }
            } else { 
              ldap_bind($ds, self::$binduser, self::$bindpass);
            }
            return $ds;
          }
          return false;
        }
  }
?>
